libobs_simple\sources\linux\sources/
linux_general_window_capture.rs

1use libobs_wrapper::{
2    data::ObsObjectBuilder,
3    runtime::ObsRuntime,
4    sources::ObsSourceRef,
5    utils::{ObsError, SourceInfo},
6};
7
8use super::DisplayServerType;
9use crate::sources::linux::{
10    sources::xcomposite_input::XCompositeInputSourceBuilder, PipeWireWindowCaptureSourceBuilder,
11};
12
13/// General Linux window capture source that automatically selects the best capture method.
14///
15/// This wrapper automatically chooses between:
16/// - **PipeWire capture** (for Wayland - captures via desktop portal with window selection)
17/// - **XComposite window capture** (for traditional X11 setups - direct window capture)
18///
19/// The selection is based on the detected display server type.
20///
21/// # Example
22///
23/// ```no_run
24/// use libobs_simple::sources::linux::LinuxGeneralWindowCapture;
25/// use libobs_wrapper::{context::ObsContext, sources::ObsSourceBuilder, utils::StartupInfo};
26///
27/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
28/// # let startup_info = StartupInfo::default();
29/// # let mut context = ObsContext::new(startup_info)?;
30/// # let mut scene = context.scene("Main Scene")?;
31///
32/// // Automatically selects PipeWire or XComposite based on display server
33/// let capture = LinuxGeneralWindowCapture::auto_detect(
34///     context.runtime().clone(),
35///     "Window Capture"
36/// )?;
37///
38/// // Add to scene
39/// scene.add(&capture)?;
40/// # Ok(())
41/// # }
42/// ```
43#[derive(Debug)]
44pub struct LinuxGeneralWindowCapture {
45    info: SourceInfo,
46    capture_type: CaptureType,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50enum CaptureType {
51    PipeWire,
52    XComposite,
53}
54
55impl LinuxGeneralWindowCapture {
56    /// Create a window capture source by auto-detecting the display server type.
57    ///
58    /// This is the recommended way to create a window capture on Linux.
59    #[must_use = "Use the 'add_to_scene' method to add the source to a scene"]
60    pub fn auto_detect(
61        runtime: ObsRuntime,
62        name: &str,
63    ) -> Result<Self, Box<dyn std::error::Error>> {
64        let display_type = DisplayServerType::detect();
65        Self::new(runtime, name, display_type)
66    }
67
68    /// Create a window capture source for a specific display server type.
69    ///
70    /// # Arguments
71    ///
72    /// * `runtime` - The OBS runtime
73    /// * `name` - Name for the source
74    /// * `display_type` - The display server type to create a source for
75    pub fn new(
76        runtime: ObsRuntime,
77        name: &str,
78        display_type: DisplayServerType,
79    ) -> Result<Self, Box<dyn std::error::Error>> {
80        if display_type.prefer_pipewire() {
81            Self::new_pipewire(runtime, name)
82        } else {
83            Self::new_xcomposite(runtime, name)
84        }
85    }
86
87    /// Create a PipeWire-based window capture source.
88    ///
89    /// Note: On Wayland, window selection is handled by the desktop portal
90    /// which will prompt the user to select a window.
91    pub fn new_pipewire(
92        runtime: ObsRuntime,
93        name: &str,
94    ) -> Result<Self, Box<dyn std::error::Error>> {
95        let builder = PipeWireWindowCaptureSourceBuilder::new(name, runtime.clone())?;
96        let info = builder.set_show_cursor(true).build()?;
97        Ok(LinuxGeneralWindowCapture {
98            info,
99            capture_type: CaptureType::PipeWire,
100        })
101    }
102
103    /// Create an XComposite-based window capture source.
104    ///
105    /// # Arguments
106    ///
107    /// * `runtime` - The OBS runtime
108    /// * `name` - Name for the source
109    pub fn new_xcomposite(
110        runtime: ObsRuntime,
111        name: &str,
112    ) -> Result<Self, Box<dyn std::error::Error>> {
113        let builder = XCompositeInputSourceBuilder::new(name, runtime.clone())?;
114        let info = builder.set_show_cursor(true).build()?;
115        Ok(LinuxGeneralWindowCapture {
116            info,
117            capture_type: CaptureType::XComposite,
118        })
119    }
120
121    /// Create an XComposite-based window capture for a specific window.
122    ///
123    /// # Arguments
124    ///
125    /// * `runtime` - The OBS runtime
126    /// * `name` - Name for the source
127    /// * `window_id` - The X11 window ID to capture
128    pub fn new_xcomposite_with_window(
129        runtime: ObsRuntime,
130        name: &str,
131        window_id: &str,
132    ) -> Result<Self, Box<dyn std::error::Error>> {
133        let builder = XCompositeInputSourceBuilder::new(name, runtime.clone())?;
134        let info = builder
135            .set_capture_window(window_id.to_string())
136            .set_show_cursor(true)
137            .build()?;
138        Ok(LinuxGeneralWindowCapture {
139            info,
140            capture_type: CaptureType::XComposite,
141        })
142    }
143
144    pub fn add_to_scene(
145        self,
146        scene: &mut libobs_wrapper::scenes::ObsSceneRef,
147    ) -> Result<ObsSourceRef, ObsError> {
148        scene.add_source(self.info)
149    }
150
151    /// Get the type of capture being used.
152    pub fn capture_type_name(&self) -> &str {
153        match self.capture_type {
154            CaptureType::PipeWire => "PipeWire",
155            CaptureType::XComposite => "XComposite",
156        }
157    }
158}
159
160impl AsRef<SourceInfo> for LinuxGeneralWindowCapture {
161    fn as_ref(&self) -> &SourceInfo {
162        &self.info
163    }
164}